#include "e.h"
#include "e_mod_main.h"
#include "compiz.h"
#include "bang.h"
#include "bang_window.h"

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xproto.h>
#include <X11/extensions/Xrandr.h>
#include <X11/cursorfont.h>
#include <X11/extensions/Xinerama.h>

#include <stdarg.h>
#include <dlfcn.h>
#include <sys/types.h>



//# define D(x)  do {printf(__FILE__ ":%d:  ", __LINE__); printf x; fflush(stdout);} while (0)
# define D(x)  ((void) 0)

//# define DN(x)  do {printf(__FILE__ ":%d:  ", __LINE__); printf x; fflush(stdout);} while (0)
# define DN(x)  ((void) 0)


static int
reallocScreenPrivate (int  size,
                      void *closure)
{
D(("BEGIN\n"));
   CompDisplay *d = (CompDisplay *) closure;
   CompScreen  *s;
   Eina_List   *l;
   void        *privates;

   for (l = d->screens;l ;l = l->next)
   {
      s = l->data;
      privates = realloc (s->privates, size * sizeof (CompPrivate));
      if (!privates)
         return FALSE;

      s->privates = (CompPrivate *) privates;
   }

   return TRUE;
}

int
allocateScreenPrivateIndex (CompDisplay *display)
{
D(("BEGIN\n"));
    return allocatePrivateIndex (&display->screenPrivateLen,
				 &display->screenPrivateIndices,
				 reallocScreenPrivate,
				 (void *) display);
}

void
freeScreenPrivateIndex (CompDisplay *display,
			int	    index)
{
D(("BEGIN\n"));
    freePrivateIndex (display->screenPrivateLen,
		      display->screenPrivateIndices,
		      index);
}

static Bool
updateDefaultIcon (CompScreen *screen)
{
D((" updateDefaultIcon (CompScreen *screen)\n"));
    CompIcon     *icon;
    char         *data;
    unsigned int width, height;

    if (!readPng (SCREEN_DEFAULT_ICON,
		  &data, &width, &height))
	return FALSE;

    icon = malloc (sizeof (CompIcon) + width * height * sizeof (CARD32));
    if (!icon)
    {
	free (data);
	return FALSE;
    }

    if (screen->defaultIcon)
    {
	finiTexture (screen, &screen->defaultIcon->texture);
	free (screen->defaultIcon);
    }

    initTexture (screen, &icon->texture);

    icon->width  = width;
    icon->height = height;

    memcpy (icon + 1, data, + width * height * sizeof (CARD32));

    screen->defaultIcon = icon;

    free (data);

    return TRUE;
}

static Bool
initPluginForScreen (CompPlugin *p,
		     CompScreen *s)
{
D(("BEGIN\n"));
    if (p->vTable->initScreen)
	return (*p->vTable->initScreen) (p, s);

    return FALSE;
}

static void
finiPluginForScreen(CompPlugin *p,
                    CompScreen *s)
{
   DF(("finiPluginForScreen [name: %s]\n", p->vTable->name));
   if (p->vTable->finiScreen)
      (*p->vTable->finiScreen) (p, s);
}


static void
frustum (GLfloat left,
	 GLfloat right,
	 GLfloat bottom,
	 GLfloat top,
	 GLfloat nearval,
	 GLfloat farval)
{
DN(("BEGIN\n"));
   GLfloat x, y, a, b, c, d;
   GLfloat m[16];

   x = (2.0 * nearval) / (right - left);
   y = (2.0 * nearval) / (top - bottom);
   a = (right + left) / (right - left);
   b = (top + bottom) / (top - bottom);
   c = -(farval + nearval) / ( farval - nearval);
   d = -(2.0 * farval * nearval) / (farval - nearval);

#define M(row,col)  m[col*4+row]
   M(0,0) = x;     M(0,1) = 0.0F;  M(0,2) = a;      M(0,3) = 0.0F;
   M(1,0) = 0.0F;  M(1,1) = y;     M(1,2) = b;      M(1,3) = 0.0F;
   M(2,0) = 0.0F;  M(2,1) = 0.0F;  M(2,2) = c;      M(2,3) = d;
   M(3,0) = 0.0F;  M(3,1) = 0.0F;  M(3,2) = -1.0F;  M(3,3) = 0.0F;
#undef M

   glMultMatrixf (m);
}

static void
perspective (GLfloat fovy,
	     GLfloat aspect,
	     GLfloat zNear,
	     GLfloat zFar)
{
DN(("BEGIN\n"));
   GLfloat xmin, xmax, ymin, ymax;

   ymax = zNear * tan (fovy * M_PI / 360.0);
   ymin = -ymax;
   xmin = ymin * aspect;
   xmax = ymax * aspect;

   frustum (xmin, xmax, ymin, ymax, zNear, zFar);
}

static void
reshape (CompScreen *s,
	 int	    w,
	 int	    h)
{
D(("reshape (CompScreen *s, %dx%d)\n",w,h));
    s->width  = w;
    s->height = h;

    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    glMatrixMode (GL_MODELVIEW);
    glLoadIdentity ();
    glDepthRange (0, 1);
    glViewport (-1, -1, 2, 2);
    glRasterPos2f (0, 0);

    s->rasterX = s->rasterY = 0;

    glViewport (0, 0, w, h);
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    perspective (60.0f, 1.0f, 0.1f, 100.0f);
    glMatrixMode (GL_MODELVIEW);

    s->region.rects = &s->region.extents;
    s->region.numRects = 1;
    s->region.extents.x1 = 0;
    s->region.extents.y1 = 0;
    s->region.extents.x2 = w;
    s->region.extents.y2 = h;
    s->region.size = 1;

    updateWorkareaForScreen (s);
}


static FuncPtr
getProcAddress (CompScreen *s,
		const char *name)
{
DN(("getProcAddress (CompScreen *s,const char *name)\n"));
    static void *dlhand = NULL;
    FuncPtr     funcPtr = NULL;

    if (s->getProcAddress)
	funcPtr = s->getProcAddress ((GLubyte *) name);

    if (!funcPtr)
    {
	if (!dlhand)
	    dlhand = dlopen (NULL, RTLD_LAZY);

	if (dlhand)
	{
	    dlerror ();
	    funcPtr = (FuncPtr) dlsym (dlhand, name);
	    if (dlerror () != NULL)
		funcPtr = NULL;
	}
    }

    return funcPtr;
}


Bool
addScreen (CompDisplay *display,
           int          screenNum,
           Window       root)
{
D(("--------------------------- BEGIN addScreen -----------------------------------\n"));
   CompScreen     *s;
   Display        *dpy = display->display;
   XVisualInfo     templ;
   XVisualInfo    *visinfo;
   GLXFBConfig    *fbConfigs;
   Window          rootReturn, parentReturn;
   Window         *children;
   unsigned int    nchildren;
   int             defaultDepth, nvisinfo, nElements, value, i;
   const char     *glxExtensions, *glExtensions;
   GLint           stencilBits;
   GLfloat         globalAmbient[]  = { 0.1f, 0.1f,  0.1f, 0.1f };
   GLfloat         ambientLight[]   = { 0.0f, 0.0f,  0.0f, 0.0f };
   GLfloat         diffuseLight[]   = { 0.9f, 0.9f,  0.9f, 0.9f };
   GLfloat         light0Position[] = { -0.5f, 0.5f, -9.0f, 1.0f };
   CompWindow     *w;
   GLXContext      shareList = 0;

   s = E_NEW(CompScreen,1);
   if (!s)
      return FALSE;

   s->windowPrivateIndices = 0;
   s->windowPrivateLen     = 0;

   if (display->screenPrivateLen)
   {
      s->privates = malloc (display->screenPrivateLen * sizeof (CompPrivate));
      if (!s->privates)
      {
         free (s);
         return FALSE;
      }
   }
   else
      s->privates = 0;

   s->display = display;

   s->damage = XCreateRegion ();
   if (!s->damage)
      return FALSE;

   s->size = 4;

   s->pendingDestroys = 0;

   s->screenNum = screenNum;
   s->colormap  = DefaultColormap (dpy, screenNum);
   s->root      = root;

   s->mapNum    = 1;

   s->damageMask  = COMP_SCREEN_DAMAGE_ALL_MASK;
   s->exposeRects = 0;
   s->sizeExpose  = 0;
   s->nExpose     = 0;

   s->rasterX = 0;
   s->rasterY = 0;

   s->windows       = NULL;

   s->stencilRef = 0x1;

   s->pendingCommands = TRUE;
    
   s->overlayWindowCount = 0;

   s->initPluginForScreen = initPluginForScreen;
   s->finiPluginForScreen = finiPluginForScreen;

   s->preparePaintScreen     = preparePaintScreen;
   s->donePaintScreen        = donePaintScreen;
   s->paintScreen            = paintScreen;
   s->paintTransformedScreen = paintTransformedScreen;
   s->paintWindow            = paintWindow;
   s->addWindowGeometry      = addWindowGeometry;
   s->drawWindowTexture      = drawWindowTexture;
   s->drawWindowGeometry     = drawWindowGeometry;
   s->damageWindowRect       = damageWindowRect;
//    s->focusWindow	      = focusWindow;
   s->setWindowScale         = setWindowScale;

    s->getProcAddress = 0;

   if (!XGetWindowAttributes (dpy, s->root, &s->attrib))
      return FALSE;
D(("addScreen: size %dx%d\n",s->attrib.width,s->attrib.height));
   s->workArea.x      = 0;
   s->workArea.y      = 0;
   s->workArea.width  = s->attrib.width;
   s->workArea.height = s->attrib.height;


   templ.visualid = XVisualIDFromVisual (s->attrib.visual);

   visinfo = XGetVisualInfo (dpy, VisualIDMask, &templ, &nvisinfo);
   if (!nvisinfo)
   {
      _berror("Couldn't get visual info for default visual.");
      return FALSE;
   }

   defaultDepth = visinfo->depth;

   glXGetConfig (dpy, visinfo, GLX_USE_GL, &value);
   if (!value)
   {
      _berror("Root visual is not a GL visual.");
      return FALSE;
   }

   glXGetConfig (dpy, visinfo, GLX_DOUBLEBUFFER, &value);
   if (!value)
   {
      _berror("Root visual is not a double buffered GL visual.");
      return FALSE;
   }

   if (display->screens)
   {
      CompScreen *scr = display->screens->data;
      shareList = scr->ctx;
   }

   s->ctx = glXCreateContext (dpy, visinfo,
                               shareList, SCREEN_INDIRECT_RENDERING);
   if (!s->ctx)
   {
      _berror("glXCreateContext failed.");
      return FALSE;
   }

   XFree (visinfo);

   if (SCREEN_INDIRECT_RENDERING)
      glxExtensions = glXQueryExtensionsString (s->display->display, 
                                                screenNum);
   else
      glxExtensions = glXQueryServerString (s->display->display, screenNum,
                                            GLX_EXTENSIONS);
   if (!strstr (glxExtensions, "GLX_EXT_texture_from_pixmap"))
   {
      _berror("GLX_EXT_texture_from_pixmap is missing.");
      return FALSE;
   }

   if (!strstr (glxExtensions, "GLX_SGIX_fbconfig"))
   {
      _berror("LX_SGIX_fbconfig is missing.");
      return FALSE;
   }

   s->getProcAddress    = (GLXGetProcAddressProc)
                           getProcAddress (s, "glXGetProcAddressARB");
   s->bindTexImage      = (GLXBindTexImageProc)
                           getProcAddress (s, "glXBindTexImageEXT");
   s->releaseTexImage   = (GLXReleaseTexImageProc)
                           getProcAddress (s, "glXReleaseTexImageEXT");
   s->queryDrawable     = (GLXQueryDrawableProc)
                           getProcAddress (s, "glXQueryDrawable");
   s->getFBConfigs      = (GLXGetFBConfigsProc)
                           getProcAddress (s, "glXGetFBConfigs");
   s->getFBConfigAttrib = (GLXGetFBConfigAttribProc)
                           getProcAddress (s, "glXGetFBConfigAttrib");
   s->createPixmap      = (GLXCreatePixmapProc)
                           getProcAddress (s, "glXCreatePixmap");

   if (!s->bindTexImage)
   {
      _berror("glXBindTexImageEXT is missing.");
      return FALSE;
   }

   if (!s->releaseTexImage)
   {
      _berror("glXReleaseTexImageEXT is missing.");
      return FALSE;
   }

   if (!s->queryDrawable     ||
       !s->getFBConfigs      ||
       !s->getFBConfigAttrib ||
       !s->createPixmap)
   {
      _berror("fbconfig functions missing.");
      return FALSE;
   }

   s->copySubBuffer = NULL;
   if (strstr (glxExtensions, "GLX_MESA_copy_sub_buffer"))
      s->copySubBuffer = (GLXCopySubBufferProc)
                          getProcAddress (s, "glXCopySubBufferMESA");

   glXMakeCurrent (dpy, s->root, s->ctx);
   currentRoot = s->root;

   glExtensions = (const char *) glGetString (GL_EXTENSIONS);

   s->textureNonPowerOfTwo = 0;
   if (strstr (glExtensions, "GL_ARB_texture_non_power_of_two"))
      s->textureNonPowerOfTwo = 1;

   glGetIntegerv (GL_MAX_TEXTURE_SIZE, &s->maxTextureSize);

   s->textureRectangle = 0;
   if (strstr (glExtensions, "GL_NV_texture_rectangle")  ||
       strstr (glExtensions, "GL_EXT_texture_rectangle") ||
       strstr (glExtensions, "GL_ARB_texture_rectangle"))
   {
      s->textureRectangle = 1;

      if (!s->textureNonPowerOfTwo)
      {
         GLint maxTextureSize;

         glGetIntegerv (GL_MAX_RECTANGLE_TEXTURE_SIZE_NV, &maxTextureSize);
         if (maxTextureSize < s->maxTextureSize)
            s->maxTextureSize = maxTextureSize;
      }
   }

   if (!(s->textureRectangle || s->textureNonPowerOfTwo))
   {
      _berror("Support for non power of two textures missing.");
      return FALSE;
   }

   s->textureEnvCombine = s->textureEnvCrossbar = 0;
   if (strstr (glExtensions, "GL_ARB_texture_env_combine"))
   {
      s->textureEnvCombine = 1;

      /* XXX: GL_NV_texture_env_combine4 need special code but it seams to
         be working anyway for now... */
      if (strstr (glExtensions, "GL_ARB_texture_env_crossbar") ||
          strstr (glExtensions, "GL_NV_texture_env_combine4"))
         s->textureEnvCrossbar = 1;
   }

   s->textureBorderClamp = 0;
   if (strstr (glExtensions, "GL_ARB_texture_border_clamp") ||
       strstr (glExtensions, "GL_SGIS_texture_border_clamp"))
      s->textureBorderClamp = 1;

   s->maxTextureUnits = 1;
   if (strstr (glExtensions, "GL_ARB_multitexture"))
   {
      s->activeTexture       = (GLActiveTextureProc)
                                getProcAddress (s, "glActiveTexture");
      s->clientActiveTexture = (GLClientActiveTextureProc)
                                getProcAddress (s, "glClientActiveTexture");

      if (s->activeTexture && s->clientActiveTexture)
         glGetIntegerv (GL_MAX_TEXTURE_UNITS_ARB, &s->maxTextureUnits);
   }

   s->fragmentProgram = 0;
   if (strstr (glExtensions, "GL_ARB_fragment_program"))
   {
      s->genPrograms    = (GLGenProgramsProc)
                           getProcAddress (s, "glGenProgramsARB");
      s->deletePrograms = (GLDeleteProgramsProc)
                           getProcAddress (s, "glDeleteProgramsARB");
      s->bindProgram    = (GLBindProgramProc)
                           getProcAddress (s, "glBindProgramARB");
      s->programString  = (GLProgramStringProc)
                           getProcAddress (s, "glProgramStringARB");
      s->programLocalParameter4f = (GLProgramLocalParameter4fProc)
                           getProcAddress (s, "glProgramLocalParameter4fARB");

      if (s->genPrograms    &&
          s->deletePrograms &&
          s->bindProgram    &&
          s->programString  &&
          s->programLocalParameter4f)
         s->fragmentProgram = 1;
   }

   s->fbo = 0;
   if (strstr (glExtensions, "GL_EXT_framebuffer_object"))
   {
      s->genFramebuffers    = (GLGenFramebuffersProc)
                               getProcAddress (s, "glGenFramebuffersEXT");
      s->deleteFramebuffers = (GLDeleteFramebuffersProc)
                               getProcAddress (s, "glDeleteFramebuffersEXT");
      s->bindFramebuffer    = (GLBindFramebufferProc)
                               getProcAddress (s, "glBindFramebufferEXT");
      s->checkFramebufferStatus = (GLCheckFramebufferStatusProc)
                               getProcAddress (s, "glCheckFramebufferStatusEXT");
      s->framebufferTexture2D = (GLFramebufferTexture2DProc)
                               getProcAddress (s, "glFramebufferTexture2DEXT");
      s->generateMipmap     = (GLGenerateMipmapProc)
                               getProcAddress (s, "glGenerateMipmapEXT");

      if (s->genFramebuffers	      &&
          s->deleteFramebuffers     &&
          s->bindFramebuffer	      &&
          s->checkFramebufferStatus &&
          s->framebufferTexture2D   &&
          s->generateMipmap)
         s->fbo = 1;
   }

   fbConfigs = (*s->getFBConfigs) (dpy,
                                   screenNum,
                                   &nElements);

   for (i = 0; i <= MAX_DEPTH; i++)
   {
      int j, db, stencil, depth, alpha, mipmap, rgba;

      s->glxPixmapFBConfigs[i].fbConfig      = NULL;
      s->glxPixmapFBConfigs[i].mipmap        = 0;
      s->glxPixmapFBConfigs[i].yInverted     = 0;
      s->glxPixmapFBConfigs[i].textureFormat = 0;

      db      = MAXSHORT;
      stencil = MAXSHORT;
      depth   = MAXSHORT;
      mipmap  = 0;
      rgba    = 0;

      for (j = 0; j < nElements; j++)
      {
         (*s->getFBConfigAttrib) (dpy,
                  fbConfigs[j],
                  GLX_ALPHA_SIZE,
                  &alpha);
         (*s->getFBConfigAttrib) (dpy,
                  fbConfigs[j],
                  GLX_BUFFER_SIZE,
                  &value);
         if (value != i && (value - alpha) != i)
            continue;

         value = 0;
         if (i == 32)
         {
            (*s->getFBConfigAttrib) (dpy,
                  fbConfigs[j],
                  GLX_BIND_TO_TEXTURE_RGBA_EXT,
                  &value);

            if (value)
            {
               rgba = 1;
D((" addScreen  s->glxPixmapFBConfigs[%d].textureFormat = GLX_TEXTURE_FORMAT_RGBA_EXT; \n",i));
               s->glxPixmapFBConfigs[i].textureFormat = GLX_TEXTURE_FORMAT_RGBA_EXT;
            }
         }

         if (!value)
         {
            if (rgba)
               continue;

            (*s->getFBConfigAttrib) (dpy,
                  fbConfigs[j],
                  GLX_BIND_TO_TEXTURE_RGB_EXT,
                  &value);
            if (!value)
               continue;
D((" addScreen  s->glxPixmapFBConfigs[%d].textureFormat = GLX_TEXTURE_FORMAT_RGB_EXT; \n",i));
            s->glxPixmapFBConfigs[i].textureFormat = GLX_TEXTURE_FORMAT_RGB_EXT;
         }

         (*s->getFBConfigAttrib) (dpy,
                  fbConfigs[j],
                  GLX_DOUBLEBUFFER,
                  &value);
         if (value > db)
            continue;

         db = value;

         (*s->getFBConfigAttrib) (dpy,
                  fbConfigs[j],
                  GLX_STENCIL_SIZE,
                  &value);
         if (value > stencil)
            continue;

         stencil = value;

         (*s->getFBConfigAttrib) (dpy,
                  fbConfigs[j],
                  GLX_DEPTH_SIZE,
                  &value);
         if (value > depth)
            continue;

         depth = value;

         if (s->fbo)
         {
            (*s->getFBConfigAttrib) (dpy,
                  fbConfigs[j],
                  GLX_BIND_TO_MIPMAP_TEXTURE_EXT,
                  &value);
            if (value < mipmap)
               continue;

            mipmap = value;
         }

         (*s->getFBConfigAttrib) (dpy,
                  fbConfigs[j],
                  GLX_Y_INVERTED_EXT,
                  &value);

         s->glxPixmapFBConfigs[i].yInverted = value;
         s->glxPixmapFBConfigs[i].fbConfig  = fbConfigs[j];
         s->glxPixmapFBConfigs[i].mipmap    = mipmap;
      }
   }

   if (nElements)
      XFree (fbConfigs);

   if (!s->glxPixmapFBConfigs[defaultDepth].fbConfig)
   {
      _berror("No GLXFBConfig for default depth.");
      return FALSE;
   }

   s->defaultIcon = NULL;
   if (!updateDefaultIcon (s))
   {
      _berror("Couldn't load default window icon.");
      return FALSE;
   }

   glGetIntegerv (GL_STENCIL_BITS, &stencilBits);
   if (!stencilBits)
   {
      _berror("No stencil buffer. Clipping of transformed "
              "windows is not going to be correct when screen is "
              "transformed.");
      return FALSE;
   }

   glClearColor (0.0, 0.0, 0.0, 1.0);
   glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
   glEnable (GL_CULL_FACE);
   glDisable (GL_BLEND);
   glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
   glColor4usv (defaultColor);
   glEnableClientState (GL_VERTEX_ARRAY);
   glEnableClientState (GL_TEXTURE_COORD_ARRAY);

   s->canDoSaturated = s->canDoSlightlySaturated = FALSE;
   if (s->textureEnvCombine && s->maxTextureUnits >= 2)
   {
      s->canDoSaturated = TRUE;
      if (s->textureEnvCrossbar && s->maxTextureUnits >= 4)
         s->canDoSlightlySaturated = TRUE;
   }

   glLightModelfv (GL_LIGHT_MODEL_AMBIENT, globalAmbient);

   glEnable (GL_LIGHT0);
   glLightfv (GL_LIGHT0, GL_AMBIENT, ambientLight);
   glLightfv (GL_LIGHT0, GL_DIFFUSE, diffuseLight);
   glLightfv (GL_LIGHT0, GL_POSITION, light0Position);

   glColorMaterial (GL_FRONT, GL_AMBIENT_AND_DIFFUSE);

   glNormal3f (0.0f, 0.0f, -1.0f);

   s->lighting = FALSE;

   if ( !XineramaIsActive( display->display ) )
   {
      s->useXinerama = FALSE;
      s->headsInfo = 0;
      s->headsCount = 0;
   }
   else
   {
      s->useXinerama = TRUE;

      XineramaScreenInfo *screen_info;
      int number;
      screen_info = XineramaQueryScreens ( display->display, &number );
      s->headsInfo = malloc ( sizeof (CompScreenHead) * number );
      s->headsCount = number;
      for ( i = 0; i < number; i++ )
      {
         s->headsInfo[i].areaX = s->headsInfo[i].x = screen_info[i].x_org;
         s->headsInfo[i].areaY = s->headsInfo[i].y = screen_info[i].y_org;
         s->headsInfo[i].areaWidth = s->headsInfo[i].width = screen_info[i].width;
         s->headsInfo[i].areaHeight = s->headsInfo[i].height = screen_info[i].height;
      }
      XFree( screen_info );
   }
   reshape (s, s->attrib.width, s->attrib.height);


   addScreenToDisplay (display, s);

   screenInitPlugins (s);

   XQueryTree (dpy, s->root,
               &rootReturn, &parentReturn,
               &children, &nchildren);
   for (i = 0; i < nchildren; i++)
      addWindow (s, children[i], i ? children[i - 1] : 0);

   Eina_List *l;
   for (l = s->windows; l; l = l->next)
   {
      w = l->data;
      if (w->attrib.map_state == IsViewable)
      {
      //   w->activeNum = s->activeNum++;
         w->damaged   = TRUE;
         w->invisible = WINDOW_INVISIBLE (w);
      }
   }

   XFree (children);

   s->filter[NOTHING_TRANS_FILTER] = COMP_TEXTURE_FILTER_FAST;
   s->filter[SCREEN_TRANS_FILTER]  = COMP_TEXTURE_FILTER_GOOD;
   s->filter[WINDOW_TRANS_FILTER]  = COMP_TEXTURE_FILTER_GOOD;
D(("-------------------------- END   addScreen -----------------------------------\n"));
   return TRUE;
}


void 
removeScreen(CompScreen *s)
{
   /* Remove all windows in screen */
   while (s->windows)
   {
      CompWindow *w = s->windows->data;
      DF(("FREE WINDOW [id: %x]\n", w->id));
      removeWindow(w);
   }
   
   /* Remove screen from display->screens list */
   s->display->screens = eina_list_remove(s->display->screens, s);
   
   /* Free Screen */
   DF(("FREE SCREEN \n"));
   DF(("  windowPrivateLen: %d\n", s->windowPrivateLen));
   
   if (s->damage)
   {
      DF(("  s->damage\n"));
      XDestroyRegion(s->damage);
   }
   if (s->colormap)
   {
      DF(("  s->colormap\n"));
      XFreeColormap(s->display->display, s->colormap);
   }
   if (s->exposeRects)
   {
      DF(("  s->exposeRects[%d]\n", s->sizeExpose));
      E_FREE(s->exposeRects);
   }
   if (s->defaultIcon)
   {
      DF(("  s->defaultIcon\n"));
      finiTexture (s, &s->defaultIcon->texture);
      E_FREE(s->defaultIcon);
   }
   if (s->ctx)
   {
      DF(("  s->ctx\n"));
      glXDestroyContext(s->display->display, s->ctx);
   }
   
   E_FREE(s);
   /*
      TODO Free also this:
   char         *windowPrivateIndices;
   int           windowPrivateLen;

   REGION        region;

   CompPrivate *privates;
   CompScreenHead *headsInfo;
   */
}

int 
screenGetHead ( CompScreen *s,
                int x,
                int y,
                int w,
                int h )
{
D(("BEGIN\n"));
    if ( !s->useXinerama ) return 1;

    int i;
    for ( i = 0; i < s->headsCount; i++ )
    {
        if ( x >= s->headsInfo[i].x &&
             x < ( s->headsInfo[i].x + s->headsInfo[i].width ) &&
             y >= s->headsInfo[i].y &&
             y < ( s->headsInfo[i].y + s->headsInfo[i].height ) )
            return i + 1;
    }

    if ( w != 0 && h != 0 )
    {
        int bestHead, maxArea, currentArea;
        int windowLeftX, windowRightX, windowTopY, windowBottomY;
        int areaLeftX, areaRightX, areaTopY, areaBottomY;
        windowLeftX = x - w / 2;
        windowRightX = x + w / 2;
        windowTopY = y - h / 2;
        windowBottomY = y + h / 2;
        bestHead = maxArea = currentArea = 0;
        for ( i = 0; i < s->headsCount; i++ )
        {
            if ( windowLeftX > ( s->headsInfo[i].x + s->headsInfo[i].width ) ||
                 windowTopY > ( s->headsInfo[i].y + s->headsInfo[i].height ) ||
                 windowRightX < s->headsInfo[i].x ||
                 windowBottomY < s->headsInfo[i].y )
                continue;
            if ( windowLeftX <= s->headsInfo[i].x ) areaLeftX = s->headsInfo[i].x;
            else areaLeftX = windowLeftX;
            if ( windowRightX >= ( s->headsInfo[i].x + s->headsInfo[i].width ) )
                areaRightX = s->headsInfo[i].x + s->headsInfo[i].width;
            else areaRightX = windowRightX;
            if ( windowTopY <= s->headsInfo[i].y ) areaTopY = s->headsInfo[i].y;
            else areaTopY = windowTopY;
            if ( windowBottomY >= ( s->headsInfo[i].y + s->headsInfo[i].height ) )
                areaBottomY = s->headsInfo[i].y + s->headsInfo[i].height;
            else areaBottomY = windowBottomY;
            currentArea = ( areaRightX - areaLeftX ) * ( areaBottomY - areaTopY );
            if ( currentArea > maxArea )
            {
                maxArea = currentArea;
                bestHead = i + 1;
            }
        }
        if ( bestHead > 0 )
            return bestHead;
    }
    return s->headsCount + 1;
}

int
screenGetHeadForWindow ( CompWindow *w )
{
D(("BEGIN\n"));
    if ( !w->screen->useXinerama ) return 1;
    return screenGetHead ( w->screen, w->serverX + w->width / 2, w->serverY + w->height / 2, w->width, w->height );
}

int
screenGetCurrentHead ( CompScreen *s )
{
D(("BEGIN\n"));
    if ( !s->useXinerama ) return 1;
    int root_x = 0, root_y = 0;
    int ignore_i;
    unsigned int ignore_ui;

    Window ignore_w;

    XQueryPointer( s->display->display,
                   s->root, &ignore_w, 
                   &ignore_w, &root_x, &root_y,
                   &ignore_i, &ignore_i, &ignore_ui );
    return screenGetHead ( s, root_x, root_y, 0, 0 );
}

int
screenGetHeadX ( CompScreen *s,
                 int head )
{
D(("BEGIN\n"));
    if ( !s->useXinerama || head > s->headsCount )
        return 0;
    return s->headsInfo[ head - 1 ].x;
}

int
screenGetHeadY ( CompScreen *s,
                 int head )
{
D(("BEGIN\n"));
    if ( !s->useXinerama || head > s->headsCount )
        return 0;
    return s->headsInfo[ head - 1 ].y;
}

int
screenGetHeadWidth ( CompScreen *s,
                     int head )
{
D(("BEGIN\n"));
    if ( !s->useXinerama || head > s->headsCount )
        return s->width;
    return s->headsInfo[ head - 1 ].width;
}

int
screenGetHeadHeight ( CompScreen *s,
                      int head )
{
D(("BEGIN\n"));
    if ( !s->useXinerama || head > s->headsCount )
        return s->height;
    return s->headsInfo[ head - 1 ].height;
}

int
screenGetHeadAreaX ( CompScreen *s,
        int head )
{
D(("BEGIN\n"));
    if ( !s->useXinerama || head > s->headsCount )
        return s->workArea.x;
    return s->headsInfo[ head - 1 ].areaX;
}

int
screenGetHeadAreaY ( CompScreen *s,
        int head )
{
D(("BEGIN\n"));
    if ( !s->useXinerama || head > s->headsCount )
        return s->workArea.y;
    return s->headsInfo[ head - 1 ].areaY;
}

int
screenGetHeadAreaWidth ( CompScreen *s,
        int head )
{
D(("BEGIN\n"));
    if ( !s->useXinerama || head > s->headsCount )
        return s->workArea.width;
    return s->headsInfo[ head - 1 ].areaWidth;
}

int
screenGetHeadAreaHeight ( CompScreen *s,
        int head )
{
D(("BEGIN\n"));
    if ( !s->useXinerama || head > s->headsCount )
        return s->workArea.height;
    return s->headsInfo[ head - 1 ].areaHeight;
}

void
damageScreenRegion (CompScreen *screen,
		    Region     region)
{
DN(("damageScreenRegion\n"));
    if (screen->damageMask & COMP_SCREEN_DAMAGE_ALL_MASK)
	return;

    XUnionRegion (screen->damage, region, screen->damage);

    screen->damageMask |= COMP_SCREEN_DAMAGE_REGION_MASK;
}

void
damageScreen (CompScreen *s)
{
D(("damageScreen \n"));
    s->damageMask |= COMP_SCREEN_DAMAGE_ALL_MASK;
    s->damageMask &= ~COMP_SCREEN_DAMAGE_REGION_MASK;
}

void
damagePendingOnScreen (CompScreen *s)
{
D(("BEGIN\n"));
    s->damageMask |= COMP_SCREEN_DAMAGE_PENDING_MASK;
}

void
forEachWindowOnScreen (CompScreen	 *screen,
                       ForEachWindowProc proc,
                       void		 *closure)
{
D(("forEachWindowOnScreen \n"));
   Eina_List *l;
   for (l = screen->windows; l; l = l->next)
      (*proc) ((CompWindow *)l->data, closure);
}

CompWindow *
findWindowAtScreen (CompScreen *s,
                    Window     id)
{
   DN(("   findWindowAtScreen (CompScreen *s, Window id)\n"));
   if (lastFoundWindow && lastFoundWindow->id == id)
   {
      D(("   findWindowAtScreen: FOUND WINDOW  id:%x\n",lastFoundWindow->id)); 	
      return lastFoundWindow;
   }
   else
   {
      CompWindow *w;
      Eina_List *l;
      for (l = s->windows; l; l = l->next)
      {
         w = l->data;
         if (w->id == id)
         {
            D(("   findWindowAtScreen: FOUND WINDOW  id:%x\n",id));
            return (lastFoundWindow = w);
         }
         D(("   findWindowAtScreen: NOT FOUND !!! id:%x\n",id)); 
      }
   }
   return 0;
}

void
insertWindowIntoScreen (CompScreen *s,
                        CompWindow *w,
                        Ecore_X_Window      aboveId)
{
   D(("     insertWindowIntoScreen (CompScreen *s,CompWindow *w,Window aboveId [%d] [or: %d])\n", aboveId, w->attrib.override_redirect));
   //~ if (!aboveId)
   //~ {
      //~ s->windows = eina_list_prepend(s->windows, w);
      //~ return;
   //~ }

   s->windows = eina_list_append(s->windows, w);
}

void
unhookWindowFromScreen (CompScreen *s,
                        CompWindow *w)
{
   D(("     unhookWindowFromScreen \n"));

   s->windows = eina_list_remove(s->windows, w);

   if (w == lastFoundWindow)
      lastFoundWindow = NULL;
   if (w == lastDamagedWindow)
      lastDamagedWindow = NULL;
}

void
updateWorkareaForScreen (CompScreen *s)
{
D(("updateWorkareaForScreen (CompScreen *s)\n"));
    CompWindow *w;
		CompScreenHead *h;
    int	       leftStrut, rightStrut, topStrut, bottomStrut, head;
    XRectangle workArea;
   Eina_List *l;

    leftStrut   = 0;
    rightStrut  = 0;
    topStrut    = 0;
    bottomStrut = 0;
    head = 0;

   if ( !s->useXinerama )
   {
      for (l = s->windows; l; l = l->next)
      {
         w = l->data;
         if (w->attrib.map_state == IsUnmapped)
            continue;
      }
   }
   else
   {
      // reset workarea for each head
      for ( head = 0; head < s->headsCount; head++ )
      {
         h = &s->headsInfo[ head ];
         h->areaX = h->x;
         h->areaY = h->y;
         h->areaWidth = h->width;
         h->areaHeight = h->height;
      }
      for (l = s->windows; l; l = l->next)
      {
         w = l->data;
         if (w->attrib.map_state == IsUnmapped)
            continue;
      }
   }
   
#define MIN_SANE_AREA 100

    if ((leftStrut + rightStrut) > (s->width - MIN_SANE_AREA))
    {
	leftStrut  = (s->width - MIN_SANE_AREA) / 2;
	rightStrut = leftStrut;
    }

    if ((topStrut + bottomStrut) > (s->height - MIN_SANE_AREA))
    {
	topStrut    = (s->height - MIN_SANE_AREA) / 2;
	bottomStrut = topStrut;
    }

    workArea.x      = leftStrut;
    workArea.y      = topStrut;
    workArea.width  = s->width  - leftStrut - rightStrut;
    workArea.height = s->height - topStrut  - bottomStrut;

    if (memcmp (&workArea, &s->workArea, sizeof (XRectangle)))
    {
	unsigned long data[4];

	data[0] = workArea.x;
	data[1] = workArea.y;
	data[2] = workArea.width;
	data[3] = workArea.height;

	XChangeProperty (s->display->display, s->root,
			 s->display->workareaAtom, XA_CARDINAL, 32,
			 PropModeReplace, (unsigned char *) data, 4);

	s->workArea = workArea;
    }
}

void
screenTexEnvMode (CompScreen *s,
		  GLenum     mode)
{
D(("BEGIN\n"));
    if (s->lighting)
	glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    else
	glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, mode);
}

void
screenLighting (CompScreen *s,
		Bool       lighting)
{
DN(("screenLighting\n"));
    if (s->lighting != lighting)
    {
	if (!SCREEN_LIGHTING)
	    lighting = FALSE;

	if (lighting)
	{
	    glEnable (GL_COLOR_MATERIAL);
	    glEnable (GL_LIGHTING);
	}
	else
	{
	    glDisable (GL_COLOR_MATERIAL);
	    glDisable (GL_LIGHTING);
	}

	s->lighting = lighting;

	screenTexEnvMode (s, GL_REPLACE);
    }
}

Ecore_X_Window
getTopWindow (CompScreen *s)
{
DN(("getTopWindow (CompScreen *s)\n"));
   CompWindow *w;
   Eina_List *l;

   /* return first window that has not been destroyed */
   for (l = eina_list_last(s->windows); l; l = l->prev)
   {
      w = l->data;
      if (w->id > 1){
         D(("getTopWindow: FOUND TOP WINDOW\n"));
         return w->id;}
   }
   D(("getTopWindow: CANT FiND TOP WINDOW\n"));
   return None;
}

void
makeScreenCurrent (CompScreen *s)
{
D(("makeScreenCurrent \n"));
    if (currentRoot != s->root)
    {
	glXMakeCurrent (s->display->display, s->root, s->ctx);
	currentRoot = s->root;

	s->pendingCommands = TRUE;
    }
}

void
finishScreenDrawing (CompScreen *s)
{
D(("finishScreenDrawing \n"));
    if (s->pendingCommands)
    {
	makeScreenCurrent (s);
	glFinish ();

	s->pendingCommands = FALSE;
    }
}
